/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Chango
 */
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;


public class Compressor {

        public Hoffman myHoffman;
	private FileInputStream reader;
	private FileInputStream reader2;
	private FileOutputStream writer;
	private int table[];
	private long numBytes;
	private String source, destiny;

        public Compressor(String source, String destiny){
            this.source=source;
            this.destiny=destiny;
            myHoffman = new Hoffman(null);
            table = new int[256];
        }

        public void readSource() throws IOException, FileNotFoundException{
            //lee el archivo de entrada para crear tabla de frecuencias
            int byteReaded;
            numBytes=0;
            reader = new FileInputStream(source);
            do{
                byteReaded=reader.read();
                if(byteReaded!=-1){
                    table[byteReaded]++;
                    numBytes++;
                }
            }while(byteReaded==-1);

            reader.close();
            createHoffman();
        }

        public void createHoffman(){
            /*crea todos los nodos con sus frecuencias y los agrega a una lista para crear
            el arbol hoffman*/
            Node aux;

            for(int i=0;i<table.length;i++){
                if(table[i]!=0){
                    aux=new Node((char)i, table[i], true);
                    myHoffman.addElement(aux);
                }
            }
            myHoffman.sort();
            myHoffman.createTree();
        }

        public void compress() throws IOException, FileNotFoundException{
            String code="",temp="",ext="",surplus="";
            int byteReaded=0;
            byte tempTable[][];

            readSource();
            reader2 = new FileInputStream(source);
            writer  = new FileOutputStream(destiny+".hfm");

            tempTable = myHoffman.getTable();
            ext = getExt(source);

            //tabla

            writer.write((byte)(numBytes%256));
            numBytes/=256;
            writer.write((byte)(numBytes%256));
            numBytes/=256;
            writer.write((byte)(numBytes%256));
            numBytes/=256;

            //extension
            for(int i=0;i<ext.length();i++){
                writer.write(ext.charAt(i));
            }
            writer.write(myHoffman.getSize());
            for(int i=0;i<myHoffman.getSize();i++){
                writer.write(tempTable[i][0]);
                code=myHoffman.getCode((byte) tempTable[i][0]);
                writer.write(binToDec(code));
                writer.write(numZero(code));
            }
            //escribir el resto
            do{
                byteReaded = reader2.read();
                if(byteReaded!=-1){
                    if(surplus.length()<8){
                        code = myHoffman.getCode((byte) byteReaded);
                    }
                    else{
                        code=surplus;
                    }
                    for(int i=0;i<code.length();i++){
                        if(temp.length()==0&&surplus.length()!=0){
                            for(int j=0;j<temp.length();j++){
                                if(surplus.length()!=0){
                                    temp+=surplus.charAt(0);
                                    surplus=surplus.substring(1);
                                }
                        else
                             break;
                            }
                        if(temp.length()==8)
                            code=surplus;
                        }
                        if(temp.length()<8){
                            temp+=code.charAt(i);
                        }
                        else{
                            surplus+=code.substring(i);
                            break;
                        }
                    }
                    if(temp.length()==8){
                        writer.write(binToDec(temp));
                        temp="";
                    }
                }
            }while(byteReaded!=1);
            if(surplus.length()!=0&&surplus.length()>=8){
                for(int k=0;k<surplus.length();k++){
                    temp+=surplus.charAt(k);
                }
            }
            if(temp.length()<8){
                for(int l=temp.length();l<8;l++){
                    temp+="0";
                }
            }
            writer.write(binToDec(temp));
            reader2.close();
        }

        private int numZero(String code){
            //regresa el numero de zeros que tendar el codigo huffman delante
            //sirve para facilitar la descompresion
		int cont=0;
		for(int i=0; i<code.length(); i++)
			if(code.charAt(i) == '0')cont++;
			else break;
		return cont;
	}

        private int binToDec(String number){
            //convierte numero binario a decimal
		int ans=0;

		for(int i=number.length()-1, j=0; i>=0; i--, j++)
			ans += ((int)number.charAt(j)-48) * Math.pow(2,i);

		return ans;
	}
        private String getExt(String root){
            //regresa extension del archivo a comprimir
		for(int i=root.length()-1; i>=0; i--)
			if(root.charAt(i)=='.')
				return root.substring(i);
		return null;
	}
}
